package public

import (
	"MoSkeleton/config/consts"
	"MoSkeleton/config/properties"
	"MoSkeleton/framework"
	fc "MoSkeleton/framework/core/consts"
	"MoSkeleton/framework/utils/captcha"
	"MoSkeleton/global"
	"MoSkeleton/services/base"
	"MoSkeleton/web/pogos/request/public"
	"context"
	"errors"
	"github.com/mojocn/base64Captcha"
	"go.uber.org/zap"
	"sync"
	"time"
)

type CaptchaService struct {
	base.BaseService
	Store  base64Captcha.Store
	driver base64Captcha.Driver
}

var once sync.Once

func (c *CaptchaService) GetCaptcha() (id, b64s, answer string, err error) {
	// 判断验证码是否开启

	captchaProperty := &global.ConfigContainerInst.AppConfig.App.Captcha
	openCaptcha := captchaProperty.Enable // 是否开启验证码
	if !openCaptcha {
		return
	}
	once.Do(func() {
		err := c.initStore(captchaProperty)
		if err != nil {
			global.MoLogger.Error("初始化验证码存储器失败", zap.Error(err))
		}
		// 字符,公式,验证码配置
		c.driver = c.createDriver(captchaProperty)
	})
	if c.driver == nil {
		err = errors.New("验证码驱动未初始化")
		return
	}
	// cp := base64Captcha.NewCaptcha(driver, store.UseWithCtx(c))   // v8下使用redis
	cp := base64Captcha.NewCaptcha(c.driver, c.Store)
	id, b64s, answer, err = cp.Generate()
	if err == nil {
		framework.MoRedis.SetEx(context.Background(), consts.RedisKey_Captcha_Id+id, time.Now().Unix(),
			time.Duration(captchaProperty.TimeOut+180)*time.Second)
	}
	return
}

func (c *CaptchaService) VerifyCaptcha(loginReq *public.LoginReq) bool {
	captchaProperty := &global.ConfigContainerInst.AppConfig.App.Captcha
	if framework.ConfigContainerInst.BootstrapConfig.Sys.Profile.Mode == fc.DebugMode && captchaProperty.UseSolidCode &&
		loginReq.Code == consts.Captcha_Solid_Code_Default {
		return true
	}
	return c.Store.Verify(loginReq.Uuid, loginReq.Code, true)
}

func (c *CaptchaService) initStore(captchaConfig *properties.CaptchaProperty) error {
	_, err := framework.SyncGroup.Do("Captcha-Store-Type", func() (interface{}, error) {
		switch captchaConfig.StoreType {
		case consts.Captcha_Store_Type_Local:
			c.Store = base64Captcha.DefaultMemStore
		case consts.Captcha_Store_Type_Redis:
			c.Store = captcha.NewDefaultRedisStore()
		default:
			c.Store = base64Captcha.DefaultMemStore
		}
		return nil, nil
	})
	return err
}

func (c *CaptchaService) createDriver(captchaProperty *properties.CaptchaProperty) base64Captcha.Driver {
	switch captchaProperty.ComplexLevel {
	case consts.Captcha_Complex_LV_Min:
		return base64Captcha.NewDriverDigit(captchaProperty.ImgHeight, captchaProperty.ImgWidth, captchaProperty.KeyLong, 0.7, 80)
	case consts.Captcha_Complex_LV_Low:
		noiseCount := captchaProperty.KeyLong
		source := captchaProperty.Source
		return base64Captcha.NewDriverString(captchaProperty.ImgHeight, captchaProperty.ImgWidth, noiseCount,
			base64Captcha.OptionShowHollowLine, captchaProperty.KeyLong, source, nil, nil, nil)
	case consts.Captcha_Complex_LV_Mid:
		noiseCount := captchaProperty.KeyLong
		source := captchaProperty.Source
		return base64Captcha.NewDriverChinese(captchaProperty.ImgHeight, captchaProperty.ImgWidth, noiseCount,
			base64Captcha.OptionShowHollowLine, captchaProperty.KeyLong, source, nil, nil, nil)
	case consts.Captcha_Complex_LV_High:
		noiseCount := captchaProperty.KeyLong
		return base64Captcha.NewDriverMath(captchaProperty.ImgHeight, captchaProperty.ImgWidth, noiseCount,
			base64Captcha.OptionShowHollowLine, nil, nil, nil)
	case consts.Captcha_Complex_LV_Max:
		return base64Captcha.NewDriverAudio(captchaProperty.KeyLong, "ch")
	}
	return nil
}
